#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;
const int MAXL = 1e6 + 10;

struct car_t {
    int d, v, a;
    bool ban;
    double l, r;
} cars[N];

int pos[MAXL];
int tree[MAXL];

int lowbit(int x)
{
    return x & (-x);
}

void update(int x)
{
    while (x <= 1000000)
    {
        tree[x]++;
        x += lowbit(x);
    }
}

int query(int x)
{
    if (x < 0) return 0;
    int res = 0;
    while (x)
    {
        res += tree[x];
        x -= lowbit(x);
    }
    return res;
}


int idx;

struct cover_t {
    double l, r;
} cover[N];

bool cmp(cover_t c1, cover_t c2)
{
    if (c1.r == c2.r) return (c1.l < c2.l);
    return (c1.r < c2.r);
}

int main()
{
    freopen("detect.in", "r", stdin);
    freopen("detect.out", "w", stdout);

    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);

    int T;
    cin >> T;
    while (T--)
    {
        memset(tree, 0, sizeof tree);

        int n, m, L, V;
        cin >> n >> m >> L >> V;
        bool flag = true;
        for (int i = 1; i <= n; i++)
        {
            cin >> cars[i].d >> cars[i].v >> cars[i].a;
            if (cars[i].a < 0) flag = false;
            if (cars[i].a == 0)
            {
                if (cars[i].v <= V)
                    cars[i].ban = false;
                else
                {
                    cars[i].ban = true;
                    cars[i].l = (double)cars[i].d, cars[i].r = (double)L;
                }
            }
            else if (cars[i].a < 0)
            {
                if (cars[i].v <= V)
                    cars[i].ban = false;
                else
                {
                    cars[i].ban = true;
                    cars[i].l = (double)cars[i].d;
                    double x = 1.0 * (cars[i].v * cars[i].v - V * V) / (-cars[i].a) / 2;
                    cars[i].r = min((double)cars[i].d + x, (double)L);
                }
            }
            else
            {
                double maxvv = (1.0 * cars[i].v * cars[i].v + 2 * cars[i].a * (L - cars[i].d));
                if (maxvv <= V * V)
                    cars[i].ban = false;
                else
                {
                    cars[i].ban = true;
                    cars[i].r = (double)L;
                    double x = 1.0 * (V * V - cars[i].v * cars[i].v) / (cars[i].a) / 2;
                    cars[i].l = max((double)cars[i].d, (double)cars[i].d + x);
                }
            }
        }

        for (int i = 1; i <= m; i++)
        {
            cin >> pos[i];
            update(pos[i]);
        }

        int ans1 = 0, idx = 0;
        for (int i = 1; i <= n; i++)
        {
            if (cars[i].ban == false) continue;
            int ll = (int)cars[i].l, rr = (int)cars[i].r;
            if (ll < cars[i].l) ll++;
            if (query(rr) - query(ll-1) == 0)
                cars[i].ban == false;
            else
            {
                ans1++;
                cover[++idx] = {cars[i].l, cars[i].r};
            }
        }

        int ans2;
        if (flag)
            ans2 = m-1;
        else
        {
            sort(cover+1, cover+idx+1, cmp);
            memset(tree, 0, sizeof tree);
            ans2 = 0;
            int cur = 1;
            for (int i = 1; i <= idx; i++)
            {
                int ll = (int)cover[i].l, rr = (int)cover[i].r;
                if (ll < cover[i].l) ll++;
                if ((query(rr) - query(ll-1)) == 0)
                {
                    cur = upper_bound(pos+1, pos+m+1, rr) - (pos+1);
                    update(pos[cur]);
                    ans2++;
                }
            }
            ans2 = (m-ans2);
        }

        cout << ans1 << " " << ans2 << endl;
    }

    return 0;
}